package com.c0der.neat.example;

import com.c0der.neat.Config;
import com.c0der.neat.Genome;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Xor {
    static class Data {
        double[] inputs;
        double[] outputs;

        public Data(double[] inputs, double[] outputs) {
            this.inputs = inputs;
            this.outputs = outputs;
        }
    }

    static Data data1 = new Data(new double[]{0, 0}, new double[]{0});

    static Data data2 = new Data(new double[]{0, 1}, new double[]{1});

    static Data data3 = new Data(new double[]{1, 0}, new double[]{1});

    static Data data4 = new Data(new double[]{1, 1}, new double[]{0});

    private static Data[] datas = new Data[]{data1, data2, data3, data4};
    private static double[][] data_inputs = new double[][]{data1.inputs, data2.inputs, data3.inputs, data4.inputs};

    public static final void run() throws Exception {
        Config config = new Config(
                2, 1,
                0.2f,
                0.7f,
                0.3f,
                0.7f,
                1000,
                -1000,
                0.7f,
                1000,
                -1000);
        List<Genome> populations = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Genome genome = new Genome(config);
            genome.mutate();
            populations.add(genome);
        }
        int generation = 0;
        while (true) {
            Genome.calculateFitness(populations, new Genome.CC() {
                @Override
                public double cc(Genome genome) {
                    double fitness = 0;
                    for (int i = 0; i < datas.length; i++) {
                        double v = genome.evaluate(datas[i].inputs)[0];
                        fitness += Math.abs(v - datas[i].outputs[0]);
                    }
                    return fitness;
                }
            });
//            for (Genome genome : populations) {
//                double fitness = 0;
//                for (int i = 0; i < datas.length; i++) {
//                    double v = genome.evaluate(datas[i].inputs)[0];
//                    fitness += Math.abs(v - datas[i].outputs[0]);
//                }
//                genome.setFitness(fitness);
//                if (fitness < 0.000001) {
//                    System.out.println("generation@" + generation + " fitness:"
//                            + genome.getFitness() + "/" + genome.getNodes().size() + "-" + genome.getConnections().size());
//                    System.out.println(genome.toString());
//                    File f = new File("save.m");
//                    genome.writeToFile(f);
//                    Genome g = Genome.readFromFile(f);
//                    return;
//                }
//            }
            populations.sort(new Comparator<Genome>() {
                @Override
                public int compare(Genome t0, Genome t1) {
                    Double v1 = Double.valueOf(t0.getFitness());
                    Double v2 = Double.valueOf(t1.getFitness());
                    int v = v1.compareTo(v2);
                    if (v == 0) {
                        v = Integer.compare(t0.getNodes().size(), t1.getNodes().size());
                        if (v == 0) {
                            v = Integer.compare(t0.getConnections().size(), t1.getConnections().size());
                        }
                    }
                    return v;
                }
            });

            Genome g1 = populations.get(0);

            if (g1.getFitness() < 0.000001) {
                System.out.println("generation@" + generation + " fitness:"
                        + g1.getFitness() + "/" + g1.getNodes().size() + "-" + g1.getConnections().size());
                System.out.println(g1.toString());
                File f = new File("save.m");
                g1.writeToFile(f);
                Genome g = Genome.readFromFile(f);
                return;
            }
            populations =  Genome.newGeneration(100, populations.subList(0, 5).toArray(new Genome[5]));
//
//            Genome g2 = populations.get(1);
//            Genome gl = populations.get(populations.size() - 1);
//            System.out.println("generation@" + generation + " fitness:"
//                    + g1.getFitness() + "/" + g1.getNodes().size() + "-" + g1.getConnections().size()
//                    + "  " + g2.getFitness() + "/" + g2.getNodes().size() + "-" + g2.getConnections().size()
//                    + "  " + gl.getFitness() + "/" + gl.getNodes().size() + "-" + gl.getConnections().size());
//            populations.clear();
//            for (int i = 0; i < 98; i++) {
//                Genome genome = Genome.x(g1, g2);
//                genome.mutate();
//                populations.add(genome);
//            }
//            populations.add(g1);
//            populations.add(g2);
            generation++;
        }
    }

    /**
     * x:实际销售百分比
     * r:实际销售百分比为0时的返回值（0.1~0.2之间随机）
     */
    private static final double fx(double x, double r) {
        return (1 - r) * Math.tanh(2f * x) + r;
    }

    public static final void main(String[] args) throws Exception {
        run();
//        double p = 0.7f;//第二段线性变化开始实际销售百分比
//        double r = Math.random() * 0.1f + 0.1f;  //随机（0.1~0.2）实际销售百分比为0时的返回值
//        for (int i = 0; i < 100; i++) {
//            double x = (double) i / 100d;
//            double y;
//            if (x >= p) {
//                double y1 = fx(p, r);
//                y = y1 + (1 - y1) * ((x - p) / (1 - p));
//            } else {
//                y = fx(x, r);
//            }
//
//            System.out.println("x:" + x + "  y:" + y);
//        }
    }
}
